package com.leyou.auth.service.impl;

import com.leyou.auth.config.JwtProperties;
import com.leyou.auth.config.PasswordConfig;
import com.leyou.auth.entity.ApplicationInfo;
import com.leyou.auth.mapper.ApplicationInfoMaapper;
import com.leyou.auth.service.IauthService;
import com.leyou.common.auth.JwtUtils;
import com.leyou.common.auth.entity.AppInfo;
import com.leyou.common.auth.entity.Payload;
import com.leyou.common.auth.entity.UserInfo;
import com.leyou.common.enums.ExceptionEnum;
import com.leyou.common.exceptions.LyException;
import com.leyou.common.utils.CookieUtils;
import com.leyou.user.client.UserClient;
import com.leyou.user.vo.UserVO;
import io.jsonwebtoken.Jwt;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author xiuer
 * @description
 * @create 2019/7/8 23:11
 */
@Service
@Slf4j
public class AuthServiceImpl implements IauthService {
    @Autowired
    private UserClient userClient;
    @Autowired
    private JwtProperties jwtProperties;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private ApplicationInfoMaapper appMapper;
    @Autowired
    private BCryptPasswordEncoder passwordEncoder;

    /**
     * 校验用户信息 并颁发令牌
     *
     * @param username
     * @param password
     */
    @Override
    public void login(String username, String password, HttpServletResponse response) {

        try {
            UserVO user = userClient.queryUserInfo(username, password);
            if (user == null) {
                throw new RuntimeException("用户名或者密码错误");
            }
            //用户登陆成功 生成用户数据，生成该用户的专属token
            UserInfo userInfo = new UserInfo(user.getId(), user.getUsername(), "GUEST");
            String token = JwtUtils.generateTokenExpireInMinutes(userInfo, jwtProperties.getPrivateKey(),
                    jwtProperties.getUser().getExpire());
            //把token返回给用户，写入cookie
            // Cookie cookie = new Cookie(jwtProperties.getUser().getCookieName(),token);
            // cookie.setDomain(jwtProperties.getUser().getDomain());
            // cookie.setPath("/");
            // cookie.setHttpOnly(true);
            // cookie.setMaxAge(-1);
            // response.addCookie(cookie);
            //使用工具类
            buildCookie(response, token);
        } catch (Exception e) {
            throw new LyException(ExceptionEnum.INVALID_USERNAME_PASSWORD);
        }

    }

    /**
     * 构建cookie
     */
    private void buildCookie(HttpServletResponse response, String token) {
        CookieUtils.newBuilder()
                .name(jwtProperties.getUser().getCookieName())
                .value(token)
                .domain(jwtProperties.getUser().getDomain())
                .httpOnly(true)
                .response(response)
                .build();
    }

    /**
     * 校验用户是否登陆
     */
    @Override
    public UserInfo verifyUser(HttpServletRequest request, HttpServletResponse response) {
        try {
            String token = CookieUtils.getCookieValue(request, jwtProperties.getUser().getCookieName());
            if (token == null) {
                throw new LyException(ExceptionEnum.UNAUTHORIZED);
            }
            //解析token
            Payload<UserInfo> payload = JwtUtils.getInfoFromToken(token, jwtProperties.getPublicKey(), UserInfo.class);
            //redis中获取黑名单
            if (redisTemplate.opsForValue().get(payload.getId()) != null) {
                throw new RuntimeException("用户已登出----token已被销毁");
            }
            //获取载荷中的过期时间
            Date expirationTime = payload.getExpiration();
            if (expirationTime.getTime() - System.currentTimeMillis() < jwtProperties.getUser().getMinRemainTime()) {
                //载荷中剩余时间为小于10min ，重新生成token
                String newToken = JwtUtils.generateTokenExpireInMinutes(payload.getUserInfo(), jwtProperties.getPrivateKey(),
                        jwtProperties.getUser().getExpire());
                buildCookie(response, newToken);
            }
            return payload.getUserInfo();
        } catch (Exception e) {
            log.error("【授权微服务】校验用户是否登陆异常 原因 {}", e.getMessage(), e);
            throw new LyException(ExceptionEnum.UNAUTHORIZED);
        }
    }

    /**
     * 注销登陆
     */
    @Override
    public void loginOut(HttpServletRequest request, HttpServletResponse response) {
        try {
            //获取token
            String token = CookieUtils.getCookieValue(request, jwtProperties.getUser().getCookieName());
            //解析token  拿到该token的id存入redis 加入黑名单
            Payload<Object> payload = JwtUtils.getInfoFromToken(token, jwtProperties.getPublicKey());
            //存入redis key 为Token的id value 随便 只要查到值就说明加入了黑名单,并写入有效时间
            long time = payload.getExpiration().getTime() - System.currentTimeMillis();
            if (time > 5000) {
                //过期时间大于5s写入redis 过期时间和token的有效期一致
                redisTemplate.opsForValue().set(payload.getId(), "blackList", time, TimeUnit.MILLISECONDS);
            }
            //删除token
            deleteCookie(jwtProperties.getUser().getCookieName(), response, jwtProperties.getUser().getDomain());
        } catch (Exception e) {
            log.error("【授权微服务】退出登陆失败 原因：{}", e.getMessage(), e);
            throw new LyException(ExceptionEnum.INVALID_NOTIFY_SIGN);
        }
    }

    private void deleteCookie(String cookieName, HttpServletResponse response, String domain) {
        Cookie cookie = new Cookie(cookieName, "");
        cookie.setMaxAge(0);
        cookie.setPath("/");
        cookie.setDomain(domain);
        response.addCookie(cookie);
    }

    /**
     * 颁发微服务令牌
     *
     * @param id
     * @param secret
     * @return
     */
    @Override
    public String authorize(Long id, String secret) {
        try {
            ApplicationInfo app = appMapper.selectByPrimaryKey(id);
            if (app == null) {
                //未查到微服务
                throw new RuntimeException("服务id或者secret错误");
            }
            //取出密钥判断是否正确
            if (!passwordEncoder.matches(secret, app.getSecret())) {
                throw new RuntimeException("服务id或者secret错误");
            }
            //微服务验证成功，构建载荷

            List<Long> longs = appMapper.queryPrivilegeList(id);
            AppInfo appInfo = new AppInfo();
            appInfo.setId(id);
            appInfo.setServiceName(app.getServiceName());
            appInfo.setTargetList(longs);
            return JwtUtils.generateTokenExpireInMinutes(appInfo, jwtProperties.getPrivateKey(),
                    jwtProperties.getApp().getAppExpire());
        } catch (Exception e) {
            log.error("【授权微服务】 生成微服务token失败! 原因 {}", e.getMessage(), e);
            throw new LyException(ExceptionEnum.INVALID_SERVER_ID_SECRET);
        }
    }
}
